﻿using System;
using System.Collections.Generic;
using System.Collections.ObjectModel;
using System.IO;
using System.Linq;
using System.Text;
using System.Threading;
using ROrganizer.Client;
using ROrganizer.Client.Crawling;
using ROrganizer.UI.Controllers.Core;
using ROrganizer.UI.Models;
using System.Windows.Forms;

namespace ROrganizer.UI.Controllers
{
    internal class CrawlerResultsFormController
    {
        private readonly FastObservableCollection<Result> result;
        private readonly AsyncWorker worker;
        private HashCredentials credentials;

        public CrawlerResultsFormController(Action<Exception> exceptioner)
        {
            this.result = new FastObservableCollection<Result>();
            this.worker = new AsyncWorker(this.OnChanged, exceptioner: exceptioner);
        }

        public event EventHandler Changed;
        private void OnChanged()
        {
            if (this.Changed != null)
                this.Changed(this, EventArgs.Empty);
        }

        public int Progress { get; private set; }
        public ObservableCollection<Result> Results
        {
            get { return this.result; }
        }

        public bool IsWorking
        {
            get { return this.worker.IsWorking; }
        }

        public bool CanCancel()
        {
            return this.worker.CanStop;
        }

        public void Cancel()
        {
            this.worker.Stop();
        }

        public bool CanReport(Result[] results)
        {
            return !this.worker.IsWorking && result.Any();
        }

        public void Report(IWin32Window owner, Result[] results)
        {
            if (this.credentials == null)
                this.credentials = ViewManager.ShowCredentials(owner);

            if (this.credentials != null)
                this.worker.Execute(this.OnReportCallback, results);
        }

        private void OnReportCallback(Result[] results)
        {
            var hGateway = new HashGateway();
            var mRepository = new MessageRepository();
            var progress = 0;

            this.Progress = 0;
            this.OnChanged();

            foreach (var result in results)
            {
                if (!hGateway.Commit(this.credentials, result.Reference.Hash, result.Reference.Imdb))
                {
                    this.credentials = null;
                    break;
                }

                result.Reference.Published = true;
                mRepository.SaveOrUpdate(result.Reference);

                this.Progress = 100 * progress++ / results.Length;
                this.OnChanged();

                this.result.Remove(result);
            }
        }

        public void Refresh()
        {
            worker.Execute(this.RefreshCallback);
        }

        private void RefreshCallback()
        {
            var iGateway = new ImdbGateway();
            var mFacility = new MetaFacility();
            var mRepository = new MessageRepository();
            var entries = mRepository.All().Where(x => !x.Published && x.Size > 600 * 1024 * 1024).ToArray();
            var progress = 0;

            this.Progress = 0;
            this.OnChanged();

            entries.AsParallel().WithDegreeOfParallelism(8).ForAll(entry =>
            {
                for (int i = 0; i < 3; i++)
                {
                    var imdb = iGateway.Query(entry.Imdb);
                    if (imdb == null || String.IsNullOrWhiteSpace(imdb.Title))
                    {
                        Thread.Sleep(TimeSpan.FromSeconds(5));
                        continue;
                    }
                    
                    var exists = File.Exists(entry.Location);
                    var result = new Result
                    {
                        Message = entry.Title,
                        Title = imdb.Title,
                        Hash = entry.Hash,
                        Size = entry.Size,
                        Rating = imdb.Rating,
                        Confidence = LetterPairSimilarity.CompareStrings(entry.Title, imdb.Title),
                        Reference = entry,
                    };

                    if (exists)
                    {
                        var meta = mFacility.AnalyzeFile(entry.Location);
                        result.Runtime = meta.Info.Runtime;
                        result.Resolution = new Resolution
                        {
                            Width = meta.Info.Width,
                            Height = meta.Info.Height
                        };
                    }

                    lock (entries)
                    {
                        this.result.Add(result);
                        this.Progress = 100 * progress++ / entries.Length;
                        this.OnChanged();
                    }

                    break;
                }
            });
        }

        public class Result
        {
            public string Message { get; set; }
            public string Title { get; set; }
            public long Size { get; set; }
            public string Hash { get; set; }
            public Resolution Resolution { get; set; }
            public int Runtime { get; set; }
            public short? Rating { get; set; }
            public double Confidence { get; set; }

            public MessageEntry Reference { get; set; }
        }
    }
}
